/**
 * 
 */
package org.swing.utility.input.util;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.InternalFrameListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.basic.BasicComboBoxEditor;

import junit.framework.Assert;

/**
 * @author Le Quynh Nhu
 *
 */
public class SwingUtil {

	public static final double GOLDEN_RATIO = 1.61803399;

	public static int PADDING_SMALL = 3;
	public static int PADDING_MEDIUM = 7;

	private static final Logger LOGGER = Logger.getLogger(SwingUtil.class
			.getName());

	/**
	 * Appends the given border to outside of the given component using
	 * {@link BorderFactory#createCompoundBorder}
	 * 
	 * @param component
	 *            to which to append the border
	 * @param border
	 *            to append
	 */
	public static void appendBorder(JComponent component, Border border) {
		appendBorder(component, border, false);
	}

	/**
	 * Appends the given border to the given component using
	 * {@link BorderFactory#createCompoundBorder}
	 * 
	 * @param component
	 *            to which to append the border
	 * @param border
	 *            to append
	 * @param inner
	 *            whether the given border should be appended on the inside
	 */
	public static void appendBorder(JComponent component, Border border,
			boolean inner) {

		if (component.getBorder() == null) {
			component.setBorder(border);
		} else {
			Border existing = component.getBorder();

			if (inner) {
				existing = BorderFactory.createCompoundBorder(existing, border);
			} else {
				existing = BorderFactory.createCompoundBorder(border, existing);
			}
			component.setBorder(existing);
		}
	}

	/**
	 * Adds a lighter color border shown on focus to the given component.
	 * 
	 * @param component
	 *            to be given a focus border
	 */
	public static void createFocusBorder(final JComponent component) {
		if (component.getBorder() == null)
			return;

		final Border normalBorder = component.getBorder();
		final Border focusBorder = new Border() {
			private RescaleOp colorConvert = new RescaleOp(1.7f, 0, null);

			public Insets getBorderInsets(Component c) {
				return normalBorder.getBorderInsets(c);
			}

			public boolean isBorderOpaque() {
				return normalBorder.isBorderOpaque();
			}

			public void paintBorder(Component c, Graphics g, int x, int y,
					int width, int height) {
				BufferedImage bi = new BufferedImage(c.getWidth(),
						c.getHeight(), BufferedImage.TYPE_INT_ARGB);
				normalBorder.paintBorder(c, bi.createGraphics(), x, y, width,
						height);
				colorConvert.filter(bi, bi);
				g.drawImage(bi, x, y, x + width, y + height, x, y, x + width, y
						+ height, null);
			}
		};
		component.addFocusListener(new FocusListener() {

			public void focusGained(FocusEvent e) {
				if (!component.getBorder().equals(focusBorder)) {
					component.setBorder(focusBorder);
				}
			}

			public void focusLost(FocusEvent e) {
				component.setBorder(normalBorder);
			}

		});
	}

	/**
	 * Creates a form field given by the specified class with a label positioned
	 * above it.
	 * 
	 * @param label
	 *            the label text
	 * @param clazz
	 *            the class of the field component
	 * @param parentPanel
	 *            the parent component to which this pair will be added
	 * @return the requested field component field instance
	 */
	public static JComponent createPairedField(String label,
			Class<? extends JComponent> clazz, JPanel parentPanel) {

		JPanel pairPanel = new JPanel(new GridLayout(0, 1));
		parentPanel.add(pairPanel);

		JLabel fieldLabel = new JLabel(label);
		fieldLabel.setFont(parentPanel.getFont().deriveFont(Font.BOLD));
		pairPanel.add(fieldLabel);

		try {
			JComponent field = (JComponent) clazz.newInstance();
			pairPanel.add(field);

			return field;
		} catch (Exception e) {
			LOGGER.log(Level.WARNING, "Unable to create paired field", e);
			return null;
		}
	}

	/**
	 * Creates a form field given by the specified class with a label positioned
	 * above it, ensuring a minimum width given by the specified number of
	 * characters.
	 * 
	 * @param label
	 *            the label text
	 * @param clazz
	 *            the class of the field component
	 * @param parentPanel
	 *            the parent component to which this pair will be added
	 * @param example
	 *            text used to estimate the proper field width
	 * @return the requested field component instance
	 */
	public static JComponent createPairedField(String label,
			Class<? extends JComponent> clazz, JPanel parentPanel,
			String example) {
		JComponent field = createPairedField(label, clazz, parentPanel);
		int width = getStringWidth(parentPanel, example)
				+ field.getInsets().left + field.getInsets().right;
		field.setPreferredSize(new Dimension(width,
				field.getPreferredSize().height));
		return field;
	}

	/**
	 * Creates a panel with a randomly colored background and a default size of
	 * 100x25.
	 * 
	 * @return a JPanel instance
	 */
	public static JPanel createPanel() {
		return createPanel(100, 25);
	}

	/**
	 * Creates a panel with a randomly colored background and the given size.
	 * 
	 * @return a JPanel instance
	 */
	public static JPanel createPanel(int width, int height) {

		Color color = Color.getHSBColor((float) Math.random(), 1f,
				(float) (Math.random() * .75f + .25f));

		JPanel panel = new JPanel();
		panel.setPreferredSize(new Dimension(width, height));
		panel.setBackground(color);
		panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

		return panel;

	}

	/**
	 * Creates a component designed for use as empty space.
	 * 
	 * @param width
	 *            spacer width
	 * @param height
	 *            spacer height
	 * @return an empty JComponent
	 */
	public static JComponent createSpacer(int width, int height) {
		JPanel panel = new JPanel();
		panel.setPreferredSize(new Dimension(width, height));
		panel.setMinimumSize(panel.getPreferredSize());
		return panel;
	}

	/**
	 * Enables anti-aliasing on an instance of Graphics.
	 * 
	 * @param g
	 *            graphics on which to force anti-aliasing
	 */
	public static void enableAntiAliasing(Graphics2D g) {
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		// g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
		// RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
	}

	/**
	 * Attempts to force the usage of a heavy-weight popup for the given
	 * ComboBox.
	 * 
	 * @param comboBox
	 *            to force having a heavy-weight popup
	 */
	public static void forceHeavyweightPopup(JComboBox comboBox) {
		// The component-taking method is preferred, but this is here because it
		// was
		// written first (keeping compatibility).
		forceHeavyweightPopup((JComponent) comboBox);
		// Setting light-weight popup enabled option doesn't affect anything if
		// its
		// parent has the heavy-weight key set but, again, it's kept for
		// compatibility.
		comboBox.setLightWeightPopupEnabled(false);
	}

	/**
	 * Attempts to force the usage of a heavy-weight popup for any JPopupMenus
	 * shown with the given component as the parent.
	 * 
	 * @param component
	 *            on which to force heavy-weight popups
	 */
	public static void forceHeavyweightPopup(JComponent component) {
		try {
			Class<?> clazz = Class.forName("javax.swing.PopupFactory");
			Field field = clazz.getDeclaredField("forceHeavyWeightPopupKey");
			field.setAccessible(true);
			component.putClientProperty(field.get(null), Boolean.TRUE);
		} catch (Exception e) {
			LOGGER.log(Level.WARNING,
					"Unable to force heavyweight popup for component", e);
		}
	}

	/**
	 * Gets the given path as an ImageIcon, useful for getting images that are
	 * stored as files inside a JAR.
	 * 
	 * @param path
	 *            path to an image file resource
	 * @return an ImageIcon of the image at the given path
	 */
	public static ImageIcon getResourceAsIcon(String path) {
		try {
			return new ImageIcon(ImageIO.read(SwingUtil.class
					.getResourceAsStream(path)));
		} catch (IOException e) {
			LOGGER.log(Level.WARNING,
					"Unable to get image resource from path: " + path, e);
		}
		return null;
	}

	/**
	 * Gets the pixel width of a String based on the given component's font.
	 * 
	 * @param component
	 * @param text
	 * @return the pixel width of the supplied String relative to the given
	 *         component
	 */
	public static int getStringWidth(JComponent component, String text) {
		return SwingUtilities.computeStringWidth(
				component.getFontMetrics(component.getFont()), text);
	}

	/**
	 * Returns a golden dimension based on the given height.
	 * 
	 * @param height
	 *            of the dimension
	 * @return a new Dimension based on the golden ration and the given height
	 */
	public static Dimension goldenDimension(int height) {
		return new Dimension((int) (height * GOLDEN_RATIO), height);
	}

	/**
	 * Removes the automatic JTable behavior to advance selection to the next
	 * row when the enter key is pressed.
	 * 
	 * @param table
	 *            to be fixed
	 */
	public static void removeEnterRowAdvance(JTable table) {
		InputMap inputMap = table
				.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
		KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
		inputMap.put(enter, new ActionMap());
	}

	/**
	 * Calls {@link #setMaximumHeight(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the maximum size
	 */
	public static void setMaxHeight(JComponent component, int height) {
		setMaximumHeight(component, height);
	}

	/**
	 * Sets the maximum dimension the given component based on its preferred
	 * width and the supplied height.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the maximum size
	 */
	public static void setMaximumHeight(JComponent component, int height) {
		component.setMaximumSize(new Dimension(
				component.getPreferredSize().width, height));
	}

	/**
	 * Calls {@link #setMaximumWidth(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the maximum size
	 */
	public static void setMaxWidth(JComponent component, int width) {
		setMaximumWidth(component, width);
	}

	/**
	 * Sets the maximum dimension the given component based on its preferred
	 * height and the supplied width.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the maximum size
	 */
	public static void setMaximumWidth(JComponent component, int width) {
		component.setMaximumSize(new Dimension(width, component
				.getPreferredSize().height));
	}

	/**
	 * Calls {@link #setMinimumHeight(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the maximum size
	 */
	public static void setMinHeight(JComponent component, int height) {
		setMinimumHeight(component, height);
	}

	/**
	 * Sets the minimum dimension the given component based on its preferred
	 * width and the supplied height.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the minimum size
	 */
	public static void setMinimumHeight(JComponent component, int height) {
		component.setMinimumSize(new Dimension(
				component.getPreferredSize().width, height));
	}

	/**
	 * Calls {@link #setMinimumWidth(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the minimum size
	 */
	public static void setMinWidth(JComponent component, int width) {
		setMinimumWidth(component, width);
	}

	/**
	 * Sets the minimum dimension the given component based on its preferred
	 * height and the supplied width.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the minimum size
	 */
	public static void setMinimumWidth(JComponent component, int width) {
		component.setMinimumSize(new Dimension(width, component
				.getPreferredSize().height));
	}

	/**
	 * Calls {@link #setPreferredHeight(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the preferred size
	 */
	public static void setPrefHeight(JComponent component, int height) {
		setPreferredHeight(component, height);
	}

	/**
	 * Sets the preferred dimension the given component based on its preferred
	 * width and the supplied height.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param height
	 *            of the preferred size
	 */
	public static void setPreferredHeight(JComponent component, int height) {
		component.setPreferredSize(new Dimension(
				component.getPreferredSize().width, height));
	}

	/**
	 * Calls {@link #setPreferredWidth(JComponent, int)}.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the preferred size
	 */
	public static void setPrefWidth(JComponent component, int width) {
		setPreferredWidth(component, width);
	}

	/**
	 * Sets the preferred dimension the given component based on its preferred
	 * height and the supplied width.
	 * 
	 * @param component
	 *            to be adjusted
	 * @param width
	 *            of the preferred size
	 */
	public static void setPreferredWidth(JComponent component, int width) {
		component.setPreferredSize(new Dimension(width, component
				.getPreferredSize().height));
	}

	/**
	 * Tries to set the system look and feel. On Linux it attempts to use gtk,
	 * and on Mac it leaves the L&F alone.
	 */
	public static void setSystemLookAndFeel() {
		if (System.getProperty("swing.defaultlaf") == null) {
			if (!System.getProperty("os.name").toLowerCase().contains("mac")) {
				if (System.getProperty("os.name").toLowerCase()
						.contains("windows")) {
					try {
						UIManager.setLookAndFeel(UIManager
								.getSystemLookAndFeelClassName());
					} catch (Exception e) {
						LOGGER.log(Level.INFO,
								"Failed to set system L&F, using default", e);
					}
				} else {
					try {
						ProcessBuilder builder = new ProcessBuilder(
								"find /usr/lib -name libgtk-x11-*.so");
						Process process = builder.start();
						InputStream is = process.getInputStream();
						if (is != null && is.read() != -1) {
							UIManager
									.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
							is.close();
						}
					} catch (Exception e) {
						LOGGER.log(Level.INFO,
								"Failed to set GTK L&F, using default", e);
					}
				}
			}
		}
	}

	public static JFileChooser createJFileChooserWithExistenceChecking() {
		return new JFileChooser() {
			public void approveSelection() {
				File[] files = selectedFiles(this);

				if (files.length == 0) {
					return;
				}

				for (int i = 0; i < files.length; i++) {
					if (!files[i].exists() && !files[i].isFile()) {
						return;
					}
				}

				super.approveSelection();
			}
		};
	}

	public static JFileChooser createJFileChooserWithOverwritePrompting() {
		return new JFileChooser() {
			public void approveSelection() {
				if (selectedFiles(this).length != 1) {
					return;
				}

				File selectedFile = selectedFiles(this)[0];

				if (selectedFile.exists() && !selectedFile.isFile()) {
					return;
				}

				if (selectedFile.exists()) {
					int response = JOptionPane.showConfirmDialog(this,
							"The file " + selectedFile.getName()
									+ " already exists. Do you "
									+ "want to replace the existing file?",
							"JUMP", JOptionPane.YES_NO_OPTION);

					if (response != JOptionPane.YES_OPTION) {
						return;
					}
				}

				super.approveSelection();
			}
		};
	}

	public static void fixEditableComboBox(JComboBox cb) {
		if (!UIManager.getLookAndFeel().getName().equals("Windows")) {
			return;
		}

		cb.setEditor(new BasicComboBoxEditor() {
			public void setItem(Object item) {
				super.setItem(item);
				editor.selectAll();
			}
		});
	}

	public static void invokeOnEventThread(Runnable r)
			throws InterruptedException, InvocationTargetException {
		if (SwingUtilities.isEventDispatchThread()) {
			r.run();
		} else {
			SwingUtilities.invokeAndWait(r);
		}
	}

	public static String nameWithoutExtension(File file) {
		String name = file.getName();
		int dotPosition = name.indexOf('.');

		return (dotPosition < 0) ? name : name.substring(0, dotPosition);
	}

	public static void removeChoosableFileFilters(JFileChooser fc) {
		FileFilter[] filters = fc.getChoosableFileFilters();

		for (int i = 0; i < filters.length; i++) {
			fc.removeChoosableFileFilter(filters[i]);
		}

		return;
	}

	public static Color toSimulatedTransparency(Color color) {
		// My guess, but it seems to work! [Jon Aquino]
		return new Color(
				color.getRed()
						+ (int) (((255 - color.getRed()) * (255 - color
								.getAlpha())) / 255d),
				color.getGreen()
						+ (int) (((255 - color.getGreen()) * (255 - color
								.getAlpha())) / 255d), color.getBlue()
						+ (int) (((255 - color.getBlue()) * (255 - color
								.getAlpha())) / 255d));
	}

	public static String truncateString(String s, int maxLength) {
		if (s.length() < maxLength) {
			return s;
		}

		return s.substring(0, maxLength - 3) + "...";
	}

	public static Point2D subtract(Point2D a, Point2D b) {
		return new Point2D.Double(a.getX() - b.getX(), a.getY() - b.getY());
	}

	public static Point2D add(Point2D a, Point2D b) {
		return new Point2D.Double(a.getX() + b.getX(), a.getY() + b.getY());
	}

	public static Point2D multiply(Point2D v, double x) {
		return new Point2D.Double(v.getX() * x, v.getY() * x);
	}

	public static Transferable getContents(Clipboard clipboard) {
		try {
			return clipboard.getContents(null);
		} catch (Throwable t) {
			return null;
		}
	}

	public static void fixClicks(final Component c) {
		// This is a time bomb because when (if?) Sun fixes the bug, this method
		// will
		// add an extra click. We should put an if statement here that
		// immediately
		// returns if the Java version is greater than or equal to that in which
		// the bug
		// is fixed. Problem is, we don't know what that version will be. [Jon
		// Aquino]
		c.addMouseListener(new MouseListener() {
			public void mousePressed(MouseEvent e) {
				add(e);
			}

			public void mouseExited(MouseEvent e) {
				add(e);
			}

			public void mouseClicked(MouseEvent e) {
				add(e);
			}

			public void mouseEntered(MouseEvent e) {
				add(e);
			}

			private MouseEvent event(int i) {
				return (MouseEvent) events.get(i);
			}

			public void mouseReleased(MouseEvent e) {
				add(e);

				if ((events.size() == 4)
						&& (event(0).getID() == MouseEvent.MOUSE_PRESSED)
						&& (event(1).getID() == MouseEvent.MOUSE_EXITED)
						&& (event(2).getID() == MouseEvent.MOUSE_ENTERED)) {
					c.dispatchEvent(new MouseEvent(c, MouseEvent.MOUSE_CLICKED,
							System.currentTimeMillis(), e.getModifiers(), e
									.getX(), e.getY(), e.getClickCount(), e
									.isPopupTrigger()));
				}
			}

			private void add(MouseEvent e) {
				if (events.size() == 4) {
					events.remove(0);
				}

				events.add(e);
			}

			private ArrayList events = new ArrayList();
		});
	}

	public static void addInternalFrameListener(JDesktopPane pane,
			final InternalFrameListener listener) {
		JInternalFrame[] frames = pane.getAllFrames();

		for (int i = 0; i < frames.length; i++) {
			frames[i].addInternalFrameListener(listener);
		}

		pane.addContainerListener(new ContainerAdapter() {
			public void componentAdded(ContainerEvent e) {
				if (e.getChild() instanceof JInternalFrame) {
					((JInternalFrame) e.getChild())
							.removeInternalFrameListener(listener);
					((JInternalFrame) e.getChild())
							.addInternalFrameListener(listener);
				}
			}
		});
	}

	public static Timer createRestartableSingleEventTimer(int delay,
			ActionListener listener) {
		Timer timer = new Timer(delay, listener);
		timer.setCoalesce(true);
		timer.setInitialDelay(delay);
		timer.setRepeats(false);

		return timer;
	}

	public static void syncEnabledStates(final JComponent c1,
			final JComponent c2) {
		c2.setEnabled(c1.isEnabled());
		c1.addPropertyChangeListener("enabled", new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				if (c1.isEnabled() == c2.isEnabled()) {
					return;
				}

				c2.setEnabled(c1.isEnabled());
			}
		});
		c2.addPropertyChangeListener("enabled", new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				if (c1.isEnabled() == c2.isEnabled()) {
					return;
				}

				c1.setEnabled(c2.isEnabled());
			}
		});
	}

	public static void sync(final JSlider s1, final JSlider s2) {
		s2.setValue(s1.getValue());
		final Boolean[] changing = new Boolean[] { Boolean.FALSE };
		s1.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (changing[0] == Boolean.TRUE) {
					return;
				}

				changing[0] = Boolean.TRUE;

				try {
					s2.setValue(s1.getValue());
				} finally {
					changing[0] = Boolean.FALSE;
				}
			}
		});
		s2.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (changing[0] == Boolean.TRUE) {
					return;
				}

				changing[0] = Boolean.TRUE;

				try {
					s1.setValue(s2.getValue());
				} finally {
					changing[0] = Boolean.FALSE;
				}
			}
		});
	}

	public static void sync(final JCheckBox c1, final JCheckBox c2) {
		c2.setSelected(c1.isSelected());

		final Boolean[] changing = new Boolean[] { Boolean.FALSE };
		c1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (changing[0] == Boolean.TRUE) {
					return;
				}

				changing[0] = Boolean.TRUE;

				try {
					c2.setSelected(c1.isSelected());
				} finally {
					changing[0] = Boolean.FALSE;
				}
			}
		});
		c2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (changing[0] == Boolean.TRUE) {
					return;
				}

				changing[0] = Boolean.TRUE;

				try {
					c1.setSelected(c2.isSelected());
				} finally {
					changing[0] = Boolean.FALSE;
				}
			}
		});
	}

	public static void setSelectedWithClick(JCheckBox checkBox, boolean selected) {
		checkBox.setSelected(!selected);
		checkBox.doClick();
	}

	public static void highlightForDebugging(JComponent component, Color color) {
		component.setBackground(color);
		component.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10,
				color));
	}

	public static File[] selectedFiles(JFileChooser chooser) {
		return ((chooser.getSelectedFiles().length == 0) && (chooser
				.getSelectedFile() != null)) ? new File[] { chooser
				.getSelectedFile() } : chooser.getSelectedFiles();
	}

	public static Component getDescendantOfClass(Class c, Container container) {
		for (int i = 0; i < container.getComponentCount(); i++) {
			if (c.isInstance(container.getComponent(i))) {
				return container.getComponent(i);
			}

			if (container.getComponent(i) instanceof Container) {
				Component descendant = getDescendantOfClass(c,
						(Container) container.getComponent(i));

				if (descendant != null) {
					return descendant;
				}
			}
		}

		return null;
	}

	public static void dispose(final JInternalFrame internalFrame,
			JDesktopPane desktopPane) {
		desktopPane.getDesktopManager().closeFrame(internalFrame);
		internalFrame.dispose();
	}

	public static void setLocation(Component componentToMove,Location location, Component other) {
		Point p = new Point(
				(int) other.getLocationOnScreen().getX()
						+ (location.fromRight ? (other.getWidth()
								- componentToMove.getWidth() - location.x)
								: location.x), (int) other
						.getLocationOnScreen().getY()
						+ (location.fromBottom ? (other.getHeight()
								- componentToMove.getHeight() - location.y)
								: location.y));
		SwingUtilities.convertPointFromScreen(p, componentToMove.getParent());
		componentToMove.setLocation(p);
	}

	public static class Location {
		private int x;
		private int y;
		private boolean fromRight;
		private boolean fromBottom;

		/**
		 * Constructor taking an initial location, offset hint.
		 *
		 * @param fromBottom
		 *            whether y is the number of pixels between the bottom edges
		 *            of the toolbox and desktop pane, or between the top edges.
		 */
		public Location(int x, boolean fromRight, int y, boolean fromBottom) {
			this.x = x;
			this.y = y;
			this.fromRight = fromRight;
			this.fromBottom = fromBottom;
		}
	}

}
